home *** CD-ROM | disk | FTP | other *** search
/ Aminet 48 / Aminet 48 (2002)(GTI - Schatztruhe)[!][Apr 2002].iso / Aminet / util / conv / DCM2ATR.lha / DCM2ATR.c next >
Encoding:
C/C++ Source or Header  |  2002-01-27  |  48.3 KB  |  1,354 lines

  1. /*****************************************************************************
  2. **
  3. **  Copyright 1998 by Ernest R. Schreurs.
  4. **  All rights reserved.
  5. **  Use of this source code is allowed under the following conditions:
  6. **  If you change anything, use a different program name.
  7. **  If you only port this code, you can use this program name.
  8. **  Apart from this, the source code is public domain.
  9. **  This program is totally free.
  10. **  Refer to the documentation for more information.
  11. **
  12. *****************************************************************************/
  13. #define MODULE  "DCM2ATR.C"
  14. /*****************************************************************************
  15. **  NAME: DCM2ATR.C
  16. **
  17. **  Author            : Ernest R. Schreurs
  18. **  Date              : February 2, 1998
  19. **  Release           : 01.01
  20. **
  21. **  Description       : This program will convert a Diskcomm file to a SIO2PC
  22. **                      disk image file, also known as ATR file.
  23. **
  24. *****************************************************************************/
  25.  
  26. /*****************************************************************************
  27. ==  INCLUDE FILES
  28. *****************************************************************************/
  29. #include <stdio.h>              /* For printf() and gets()              */
  30. #include <ctype.h>              /* For isalnum() and toupper()          */
  31. #include <stdlib.h>             /* For the exit function                */
  32. #include <string.h>             /* String and memory stuff              */
  33.  
  34. /*****************************************************************************
  35. ==  DEFINED SYMBOLS
  36. *****************************************************************************/
  37.  
  38. #ifndef FALSE
  39. #define FALSE               0
  40. #endif
  41. #ifndef TRUE
  42. #define TRUE                1
  43. #endif
  44.  
  45. #ifndef NULL
  46. #define NULL                0
  47. #endif
  48. #define PATH_LEN            128             /* Maximum path length          */
  49.  
  50. #define BUF_LEN             80              /* fgets buffer length          */
  51. #define SUCCESS             1               /* Success is non-zero          */
  52. #define FAILURE             0               /* Failure is zero              */
  53.  
  54. /*
  55. **  These are the compression types used to indicate the type of record.
  56. */
  57. #define DCM_CHANGE_BEGIN    0x41            /* Change only start of sector  */
  58. #define DCM_DOS_SECTOR      0x42            /* 128 byte compressed sector   */
  59. #define DCM_COMPRESSED      0x43            /* Uncompressed/compressed pairs*/
  60. #define DCM_CHANGE_END      0x44            /* Change only end of sector    */
  61. #define DCM_FLUSH_BUFFER    0x45            /* Flush the buffer             */
  62. #define DCM_SAME_AS_BEFORE  0x46            /* Same as previous non-zero    */
  63. #define DCM_UNCOMPRESSED    0x47            /* Uncompressed sector          */
  64.  
  65. /*
  66. **  These are the density types.
  67. */
  68. #define DENSITY_SD          1               /* Single density, 90K          */
  69. #define DENSITY_DD          2               /* Double density, 180K         */
  70. #define DENSITY_ED          3               /* Enhanced density, 130K       */
  71.  
  72. /*****************************************************************************
  73. ==  MACRO DEFINITIONS
  74. *****************************************************************************/
  75. /*
  76. **  Macro for casting stuff to requirements of stupid
  77. **  standard library functions.
  78. */
  79.  
  80. #define FGETS( buf, buf_len, file_ptr )                                 \
  81.     (void *)fgets( (char *)buf, (int)buf_len, (FILE *)file_ptr )
  82.  
  83. #define STRLEN( str )                                                   \
  84.     strlen( (const char *)(str) )
  85.  
  86. /*
  87. **  Macro for getting input from the terminal,
  88. **  allowing the user to exit with either control Z or
  89. **  inputting the string ^Z to indicate intention of
  90. **  terminating the program.
  91. */
  92. #define GET_BUF()                                                           \
  93. {                                                                           \
  94.     if ( FGETS( buf, BUF_LEN, stdin )  ==  NULL )                           \
  95.     {                                                                       \
  96.         printf( "Terminated by ^Z\n" );                                     \
  97.         panic(0);                                                           \
  98.     }                                                                       \
  99.     if ( memcmp( buf, "^Z", 2 ) == 0 || memcmp( buf, "^z", 2 ) == 0  )      \
  100.     {                                                                       \
  101.         printf( "Terminated by ^Z\n" );                                     \
  102.         panic(0);                                                           \
  103.     }                                                                       \
  104. }
  105.  
  106. #define PRINT( lst )                                                        \
  107. {                                                                           \
  108.     if( diagnostics )                                                       \
  109.     {                                                                       \
  110.         printf lst;                                                         \
  111.     }                                                                       \
  112. }
  113.  
  114. /*****************************************************************************
  115. ==  TYPE and STRUCTURE DEFINITIONS
  116. *****************************************************************************/
  117. typedef     unsigned char   bool;   /* Boolean value                        */
  118. typedef     unsigned char   ubyte;  /* Exactly eight bits, unsigned         */
  119. typedef     short           int16;  /* At least 16 bits, signed             */
  120. typedef     unsigned short  uint16; /* At least 16 bits, unsigned           */
  121. typedef     long            int32;  /* At least 32 bits, signed             */
  122. typedef     unsigned long   uint32; /* At least 32 bits, unsigned           */
  123.  
  124. /*
  125. **  SIO2PC image file header.
  126. */
  127. typedef struct
  128. {
  129.     ubyte       atr_hdr_lo;             /* Header, sum of NICKATARI, low    */
  130.     ubyte       atr_hdr_hi;             /* and high bytes of the word       */
  131.     ubyte       atr_siz_lo;             /* Size of atr excluding header,    */
  132.     ubyte       atr_siz_hi;             /* expressed in paragraphs, low/high*/
  133.     ubyte       atr_sec_size_lo;        /* Sector size, low byte            */
  134.     ubyte       atr_sec_size_hi;        /* Sector size, high byte           */
  135.     ubyte       atr_hi_size_lo;         /* High order portion of 32 bit size*/
  136.     ubyte       atr_hi_size_hi;         /* of atr, low/high                 */
  137.     ubyte       atr_d_info;             /* Copy protect/write protect flags */
  138.     ubyte       atr_sec_lo;             /* Sector number of typical copy    */
  139.     ubyte       atr_sec_hi;             /* protected sector, low/high       */
  140.     ubyte       atr_tunused[5];         /* Unused spare bytes               */
  141. } atr_blk;
  142.  
  143. /*****************************************************************************
  144. ==  IMPORTED VARIABLES
  145. *****************************************************************************/
  146. /*****************************************************************************
  147. ==  LOCAL ( HIDDEN ) VARIABLES
  148. *****************************************************************************/
  149. static FILE *   atr_file;               /* SIO2PC disk image file           */
  150. static FILE *   dcm_file;               /* Diskcomm input file              */
  151. static ubyte    atr_path[PATH_LEN];     /* Output atr file spec             */
  152. static ubyte    dcm_path[PATH_LEN];     /* Input dcm file spec              */
  153.  
  154. static ubyte    sec_buf[256];           /* The sector buffer                */
  155. static ubyte    sec_zer[256];           /* A sector buffer with zeroes      */
  156.  
  157. static uint32   cnt_41;                 /* Number of 0x41 blocks            */
  158. static uint32   cnt_42;                 /* Number of 0x42 blocks            */
  159. static uint32   cnt_43;                 /* Number of 0x43 blocks            */
  160. static uint32   cnt_44;                 /* Number of 0x44 blocks            */
  161. static uint32   cnt_45;                 /* Number of 0x45 blocks            */
  162. static uint32   cnt_46;                 /* Number of 0x46 blocks            */
  163. static uint32   cnt_47;                 /* Number of 0x47 blocks            */
  164. static uint32   cnt_F9;                 /* Number of 0xF9 headers           */
  165. static uint32   cnt_FA;                 /* Number of 0xFA headers           */
  166.  
  167. static int      dcm_byte;               /* Byte from the Diskcomm file      */
  168. static ubyte    density;                /* Density type                     */
  169. static bool     diagnostics;            /* Print diagnostic data            */
  170. static ubyte    file_type;              /* File type from header            */
  171. static uint16   increment;              /* Byte of path to increment        */
  172. static bool     large;                  /* Large file/small files           */
  173. static bool     last;                   /* Last pass                        */
  174. static uint16   max_sector;             /* Highest sector number            */
  175. static ubyte    pass;                   /* Pass count                       */
  176. static ubyte    pass_cnt;               /* Pass counter                     */
  177. static ubyte    pass_expected;          /* Pass count we should be at       */
  178. static uint32   sec_size;               /* Sector size                      */
  179. static uint16   sector;                 /* Sector number                    */
  180. static uint16   sector_atr;             /* Sector number written to atr file*/
  181. static uint16   sectors;                /* Number of sectors on disk        */
  182. static bool     seq_flag;               /* Sequential sector flag           */
  183. static bool     statistics;             /* Print statistics information     */
  184. static ubyte    type;                   /* Type of compression              */
  185.  
  186. /*****************************************************************************
  187. ==  EXPORTED VARIABLES
  188. *****************************************************************************/
  189. /*****************************************************************************
  190. ==  IMPORTED FUNCTIONS
  191. *****************************************************************************/
  192. /*****************************************************************************
  193. ==  LOCAL ( HIDDEN ) FUNCTIONS
  194. *****************************************************************************/
  195. static void     cleanup( void );
  196. static uint32   dcm2atr( void );
  197. static ubyte    get_byte( void );
  198. static void     panic( int error_level );
  199. static uint32   process_header( void );
  200. static void     usage( char * cmd );
  201.  
  202. /*****************************************************************************
  203. ==  EXPORTED FUNCTIONS
  204. *****************************************************************************/
  205. int                 main();                 /* Normal entry point to it all */
  206.  
  207. /*****************************************************************************
  208. ==  LOCAL ( HIDDEN ) FUNCTIONS
  209. *****************************************************************************/
  210.  
  211. /*****************************************************************************
  212. **  NAME:  cleanup()
  213. **
  214. **  PURPOSE:
  215. **      Cleanup any mess that was created.
  216. **
  217. **  DESCRIPTION:
  218. **      This function will attempt to close all open files and
  219. **      free allocated memory.
  220. **
  221. **  INPUT:
  222. **      - The file pointers and paths are used.
  223. **
  224. **  OUTPUT:
  225. **      The function returns nothing.
  226. **
  227. */
  228.  
  229. static void         cleanup( void )
  230. {
  231.     if( dcm_file )
  232.     {
  233.         fclose( dcm_file );
  234.     }
  235.     if( atr_file )
  236.     {
  237.         fclose( atr_file );
  238.     }
  239.     return;
  240. }
  241.  
  242. /*****************************************************************************
  243. **  NAME:  dcm2atr()
  244. **
  245. **  PURPOSE:
  246. **      Read data from the .dcm file and convert it to sectors in the ATR file.
  247. **
  248. **  DESCRIPTION:
  249. **      This function will read the data and process it.
  250. **
  251. **  INPUT:
  252. **      Nothing.
  253. **      Data is taken from the dcm file.
  254. **
  255. **  OUTPUT:
  256. **      Writes the .atr file.
  257. **      Prints results.
  258. **      Returns SUCCESS if data converted successfully.
  259. **      Returns FAILURE if some error occurred.
  260. **
  261. */
  262.  
  263. static uint32       dcm2atr( void )
  264. {
  265.     uint16          bytes_done;             /* Number of bytes filled       */
  266.     uint16          end_offset;             /* Offset of end of string      */
  267.     ubyte           fill_char;              /* Fill character               */
  268.     uint16          level;                  /* Interpreted level of signal  */
  269.     uint16          offset;                 /* Offset into sector           */
  270.  
  271. /*
  272. **  The sequential flag tells us whether or not the next sector data is
  273. **  related to the next sector on the disk.  Sectors that are all zeroes are
  274. **  not stored in the .dcm file, so this is indicated by a flag.  The next
  275. **  sector number is then stored prior to that sector data.  Get the
  276. **  sector number if the next sector is not the next sector in sequence.
  277. */
  278.     do                          /* Until we find a flush buffer code */
  279.     {
  280.         if( seq_flag )
  281.         {
  282.             sector++;           /* Next sequential sector */
  283.             PRINT( ("Sequential sector: %u\n", sector ) );
  284.         }
  285.         else
  286.         {
  287.             sector = get_byte();
  288.             sector += ( ((uint16)get_byte()) << 8 );
  289.             PRINT( ("Jump to sector: %u\n", sector ) );
  290.         }
  291.  
  292.         type = get_byte();
  293.         seq_flag = ( type & 0x080 ) ? TRUE : FALSE;
  294.         type &= 0x7F;
  295.  
  296.         PRINT( ("Type: %.2x sector: %u\n", type, sector ) );
  297.  
  298.         switch( type )
  299.         {
  300.             case DCM_CHANGE_BEGIN:      /* 0x41 */
  301.                 cnt_41++;
  302.                 offset = get_byte();
  303.                 do                      /* Reverse copy to beginning    */
  304.                 {
  305.                     sec_buf[offset] = get_byte();
  306.                 } while( offset-- );
  307.                 PRINT( ("Beginning of sector reached\n") );
  308.                 break;
  309.             case DCM_DOS_SECTOR:        /* 0x42 */
  310.                 cnt_42++;
  311.                 for( offset = 123; offset < 128; offset++ )
  312.                 {
  313.                     sec_buf[offset] = get_byte();
  314.                 }
  315.                 memset( sec_buf, sec_buf[123], 123 );
  316.                 break;
  317.             case DCM_COMPRESSED:        /* 0x43 */
  318.                 cnt_43++;
  319.                 for( offset = 0, bytes_done = 0; bytes_done < sec_size; )
  320.                 {
  321.                     end_offset = get_byte();
  322.                     while( offset != end_offset )
  323.                     {
  324.                         sec_buf[offset] = get_byte();
  325.                         offset = (++offset) & 0x0FF; /* Roll over 255 to 0 */
  326.                         bytes_done++;
  327.                         if( bytes_done == sec_size )
  328.                             break;
  329.                     } /* end while uncompressed string */
  330.                     PRINT( ("Uncompressed end\n") );
  331.                     if( bytes_done < sec_size )
  332.                     {
  333.                         end_offset = get_byte();
  334.                         fill_char = get_byte();
  335.                         do /* while end of compressed string not reached */
  336.                         {
  337.                             sec_buf[offset] = fill_char;
  338.                             offset = (++offset) & 0x0FF;
  339.                             bytes_done++;
  340.                             if( bytes_done == sec_size )
  341.                                 break;
  342.                         } while( offset != end_offset );
  343.                         PRINT( ("Compressed end\n") );
  344.                     } /* End if space left in sector */
  345.                 } /* end for uncompressed/compressed pairs */
  346.                 break;
  347.             case DCM_CHANGE_END:        /* 0x44 */
  348.                 cnt_44++;
  349.                 offset = get_byte();
  350.                 do                      /* Copy bytes up to the end         */
  351.                 {
  352.                     sec_buf[offset++] = get_byte();
  353.                 } while( offset < sec_size );
  354.                 PRINT( ("End of sector reached\n") );
  355.                 break;
  356.             case DCM_FLUSH_BUFFER:      /* 0x45 */
  357.                 cnt_45++;
  358.                 break;
  359.             case DCM_SAME_AS_BEFORE:    /* 0x46 */
  360.                 cnt_46++;
  361.                 break;
  362.             case DCM_UNCOMPRESSED:      /* 0x47 */
  363.                 cnt_47++;
  364.                 for( offset = 0; offset < sec_size; offset++ )
  365.                 {
  366.                     sec_buf[offset] = get_byte();
  367.                 }
  368.                 break;
  369.             default:
  370.                 fprintf(stderr, "\n%s is not a valid Diskcomm archive,\n", dcm_path);
  371.                 fprintf(stderr, "or archive damaged, bad record type encountered.\n");
  372.                 if( !large )
  373.                     printf("Did you forget the /B switch in the COPY command?\n");
  374.                 panic( 255 );
  375.                 break;
  376.         }
  377.         if( type != DCM_FLUSH_BUFFER )
  378.         {
  379.  
  380. /*
  381. **  Write the sector data to the atr file.  If we skipped sectors, we must
  382. **  fill these sectors with zeroes, and write them to the file first.
  383. **  Remember that the first three sectors are always 128 bytes.
  384. */
  385.             for( sector_atr++; sector_atr < sector; sector_atr++ )
  386.             {
  387.                 fwrite( sec_zer, 1, (sector_atr < 4) ? 128 : sec_size, atr_file );
  388.             }
  389.             fwrite( sec_buf, 1, (sector_atr < 4) ? 128 : sec_size, atr_file );
  390.         }
  391.     } while( type != DCM_FLUSH_BUFFER );
  392. }
  393.  
  394. /*****************************************************************************
  395. **  NAME:  get_byte()
  396. **
  397. **  PURPOSE:
  398. **      Get one byte from the .dcm file.
  399. **
  400. **  DESCRIPTION:
  401. **      This function will read one byte from the .dcm file, and return
  402. **      it as an unsigned byte.
  403. **
  404. **  INPUT:
  405. **      - The diskcomm file.
  406. **
  407. **  OUTPUT:
  408. **      The function returns an unsigned byte.
  409. **
  410. */
  411.  
  412. static ubyte        get_byte( void )
  413. {
  414.  
  415. /*
  416. **  Get the next byte from the Diskcomm file.
  417. **  If we get an End Of File condition, this is an error, since we never
  418. **  call this function unless we are sure we need more bytes.
  419. */
  420.     dcm_byte = fgetc( dcm_file );
  421.  
  422.     if( dcm_byte == EOF )
  423.     {
  424.         fprintf(stderr, "\n%s is not a valid Diskcomm archive,\n", dcm_path);
  425.         fprintf(stderr, "premature end of file encountered.\n");
  426.         if( !large )
  427.         {
  428.             printf("Did you merge all parts of this Diskcomm archive with the COPY command?\n");
  429.             printf("Did you forget the /B switch in the COPY command?\n");
  430.             printf("Did you copy the files in the proper order?\n");
  431.             printf("Did you read the documentation?\n");
  432.         }
  433.         panic( 255 );
  434.     }
  435.  
  436.     PRINT( ("get_byte returns %.2x\n", dcm_byte ) );
  437.  
  438.     return( ((ubyte)(dcm_byte & 0x0FF)) );
  439. }
  440.  
  441. /*****************************************************************************
  442. **  NAME:  panic()
  443. **
  444. **  PURPOSE:
  445. **      Abort all processing and terminate program.
  446. **
  447. **  DESCRIPTION:
  448. **      This function will attempt to close all open files and
  449. **      free allocated memory.  It will then terminate the program.
  450. **
  451. **  INPUT:
  452. **      - The error level code to be handed to the Operating System.
  453. **
  454. **  OUTPUT:
  455. **      The function returns nothing.  In fact, it does not return at all.
  456. **
  457. */
  458.  
  459. static void         panic( error_level )
  460. int                 error_level;            /* Error level value to pass    */
  461. {
  462.     cleanup();
  463.     exit( error_level );
  464. }
  465.  
  466. /*****************************************************************************
  467. **  NAME:  process_header()
  468. **
  469. **  PURPOSE:
  470. **      Process the data from the header of the .dcm file and store
  471. **      relevant information.
  472. **
  473. **  DESCRIPTION:
  474. **      This function will read the relevant data about the .dcm file
  475. **      and store it.
  476. **
  477. **  INPUT:
  478. **      Nothing.
  479. **      Data is read from the Diskcomm file.
  480. **
  481. **  OUTPUT:
  482. **      Prints results.
  483. **      Stores data related to the format and contents of the Diskcomm file.
  484. **      Returns SUCCESS if header was processed successfully.
  485. **      Returns FAILURE if some error occurred.
  486. **
  487. */
  488.  
  489. static uint32       process_header( void )
  490. {
  491.  
  492. /*
  493. **  Diskcomm files always start with either $FA or $F9.
  494. **  Files that start with $F9 are multiple file archives.
  495. **  Files that start with $FA are single file archives.
  496. **  The second byte holds the following pieces of information:
  497. **  The high order bit indicates whether or not this is the last pass.
  498. **  The next two bits specifies the disk format of the original disk.
  499. **  00 is single denisty, 01 is double density, 10 is enhanced density.
  500. **  The last 5 bits indicate the pass count value.
  501. **  After this, we always find a two-byte sector number,
  502. **  stored in the 6502 low byte/high byte format.
  503. **  See below for more details.
  504. */
  505.  
  506. /*
  507. **  Get the byte that specifies the file type.
  508. **  We might have to skip some bytes that were added by file transfers.
  509. **  If this is the first header, we are reading the first byte, and we
  510. **  cannot be expected to process junk, so then we must get either F9 or FA.
  511. */
  512.     if( cnt_45 == 0 )   /* First header? */
  513.     {
  514.         file_type = get_byte();
  515.         if( ( file_type != 0x0F9 ) && ( file_type != 0x0FA ) )
  516.         {
  517.             fprintf(stderr, "\n%s is not a valid Diskcomm archive,\n", dcm_path);
  518.             fprintf(stderr, "it does not begin with F9 or FA.\n");
  519.             return( FAILURE );
  520.         }
  521.         memset( sec_buf, 0x00, 256 );   /* Initialize sector buffer */
  522.     } /* end if first header */
  523.     else
  524.     {
  525.  
  526. /*
  527. **  The last thing we read from the file was the flush buffer code.
  528. **  If this is an archive that consists of only one file, we must now
  529. **  find the header for the first pass.  If it is a multi file archive,
  530. **  there are two possibilities.  Either we will now reach the end of the
  531. **  file, and we must close it and open the next file, or the user somehow
  532. **  merged the files, and we can continue reading this file.  Either way,
  533. **  file transfer may have added bytes to the end of the archive file.
  534. **  Read bytes until we hit the header byte, or until we hit the end of
  535. **  file marker.  At end of file, try to open the next file.
  536. */
  537.         do      /* Read bytes until we find a header byte */
  538.         {
  539.             dcm_byte = fgetc( dcm_file );
  540.             file_type = ((ubyte)(dcm_byte & 0x0FF));
  541.             PRINT( ("Looking for header byte found %.2x\n", file_type ) );
  542.             if( dcm_byte == EOF )
  543.             {
  544.                 fclose( dcm_file );
  545.                 (dcm_path[increment])++;
  546.                 dcm_file = fopen( (char *)dcm_path, "rb" );
  547.                 PRINT( ("Attempting to open %s.\n", dcm_path ) );
  548.                 if( dcm_file == NULL )
  549.                 {
  550.                     fprintf(stderr, "\nCannot open next Diskcomm archive file %s.\n",
  551.                             dcm_path );
  552.                     return( FAILURE );
  553.                 }
  554.                 dcm_byte = fgetc( dcm_file );
  555.                 if( dcm_byte == EOF )
  556.                 {
  557.                     fprintf(stderr, "\nNext file %s\n", dcm_path);
  558.                     fprintf(stderr, "is not a valid .dcm file, it does not begin with F9 or FA.\n");
  559.                     return( FAILURE );
  560.                 }
  561.                 file_type = ((ubyte)(dcm_byte & 0x0FF));
  562.                 PRINT( ("Looking for header byte found %.2x\n", file_type ) );
  563.                 if( ( file_type != 0x0F9 ) && ( file_type != 0x0FA ) )
  564.                 {
  565.                     fprintf(stderr, "\nNext file %s\n", dcm_path);
  566.                     fprintf(stderr, "is not a valid .dcm file, it does not begin with F9 or FA.\n");
  567.                     return( FAILURE );
  568.                 }
  569.             }
  570.         } while( ( file_type != 0x0F9 ) && ( file_type != 0x0FA ) );
  571.     } /* End else if first header */
  572.  
  573.     if( file_type == 0x0F9 )
  574.     {
  575.         large = FALSE;
  576.         cnt_F9++;
  577.         memset( sec_buf, 0x00, 256 );   /* Initialize sector buffer */
  578.     }
  579.     else
  580.     {
  581.         large = TRUE;
  582.         cnt_FA++;
  583.     }
  584.  
  585. /*
  586. **  Get the byte that specifies the pass count and the disk format.
  587. **  If the high order bit is set, this is the last pass.
  588. */
  589.     pass = get_byte();
  590.     last = ( pass & 0x080 ) ? TRUE : FALSE;
  591.     density = (( pass & 0x60 ) >> 5 ) + 1;
  592.     pass &= 0x1F;
  593.  
  594. /*
  595. **  The pass count should match the expected pass count, otherwise
  596. **  this archive is corrupt.  In this case it was probably assembled out
  597. **  of multiple smaller archive files.  Concatenating them in the proper
  598. **  order is required for this program.
  599. */
  600.     if( pass != pass_expected )
  601.     {
  602.         fprintf(stderr, "\nThe header indicated an out of sequence pass number.");
  603.         fprintf(stderr, "\n%s is not a valid Diskcomm archive.\n", dcm_path);
  604.         if( !large )
  605.         {
  606.             printf("Did you merge all parts of this Diskcomm archive with the COPY command?\n");
  607.             printf("Did you forget the /B switch in the COPY command?\n");
  608.             printf("Did you copy the files in the proper order?\n");
  609.             printf("Did you read the documentation?\n");
  610.         }
  611.         return( FAILURE );
  612.     }
  613.  
  614. /*
  615. **  Well, we processed the header.
  616. **  This looks like what we wanted, so we will call this success.
  617. */
  618.  
  619.     return( SUCCESS );
  620. }
  621.  
  622. /*****************************************************************************
  623. **  NAME:  usage()
  624. **
  625. **  PURPOSE:
  626. **      Display the command line format for the program.
  627. **
  628. **  DESCRIPTION:
  629. **      This function will explain the usage of the program to the user.
  630. **      The program name is taken from the first command line argument.
  631. **
  632. **  INPUT:
  633. **      - The address of the command line, containing the program name.
  634. **
  635. **  OUTPUT:
  636. **      The usage is displayed on the terminal.
  637. **      The function returns nothing.
  638. **
  639. */
  640.  
  641. static void         usage( cmd )
  642. char * cmd;                         /* Program name                     */
  643. {
  644.     char * whoami;      /* For searching program name in command line   */
  645.     char * name;        /* Pointer to actual program name in command    */
  646.     int    len;         /* Length of program name                       */
  647.     int    found_dot;   /* Nonzero if we found a dot in the name        */
  648.  
  649. /*
  650. **  Get program name and print usage message.
  651. **  The complete pathname including extension is part of the first
  652. **  argument as passed by the operating system.
  653. */
  654.     for( whoami = cmd, len = 0, found_dot = 0; *whoami; whoami++ )
  655.     {
  656.         if( *whoami == '.' )
  657.         {
  658.             found_dot = 1;
  659.             continue;
  660.         }
  661.  
  662. /*
  663. **  If this was part of the path
  664. */
  665.         if( ( *whoami == '\\' ) || ( *whoami == '/' ) )
  666.         {
  667.             name = whoami + 1;  /* record position */
  668.             len = 0;            /* then restart counting length */
  669.             found_dot = 0;
  670.             continue;
  671.         }
  672.         if( *whoami == ' ' )    /* end of name found            */
  673.             break;
  674.         if( found_dot )         /* skip .exe or .com stuff      */
  675.             continue;
  676.         len++;                  /* Increment program name length */
  677.     }
  678.  
  679. /*
  680. **  Let me explain...
  681. */
  682.     fprintf(stderr, "\nUsage: %.*s [DCM file] [ATR file] [/d] [/h=nnnn] [/s]\n", len, name);
  683.     fprintf(stderr, "to convert a .dcm file to an .atr disk image.\n\n");
  684.     fprintf(stderr, "DCM file   a file created by the Diskcomm program.\n");
  685.     fprintf(stderr, "ATR file   a file in the SIO2PC disk image format.\n\n");
  686.     fprintf(stderr, "/d         to print diagnostic information.\n");
  687.     fprintf(stderr, "/h=nnnn    highest sector number to expect,\n");
  688.     fprintf(stderr, "           where nnnn is a number from 1 to 9999.\n");
  689.     fprintf(stderr, "/s         to print statistics information.\n\n");
  690.     fprintf(stderr, "Refer to the documentation for more information.\n");
  691.  
  692.     return;
  693. }
  694.  
  695. /*****************************************************************************
  696. ==  EXPORTED FUNCTIONS
  697. *****************************************************************************/
  698.  
  699. /*****************************************************************************
  700. **  NAME:  MAIN()
  701. **
  702. **  PURPOSE:
  703. **      An entry point for testing or running this utility.
  704. **
  705. **  DESCRIPTION:
  706. **      Prompt for the file to open and then go process it.
  707. **
  708. **  INPUT:
  709. **      argc and argv.
  710. **
  711. **  OUTPUT:
  712. **      Returns an int as it should.
  713. **
  714. */
  715. int                 main( argc, argv )
  716. int                 argc;               /* Command line argument count  */
  717. char              * argv[];             /* Command line argument ptrs   */
  718. {
  719.     ubyte           answer;                 /* Response to yes/no question  */
  720.     uint32          arg_ndx;                /* Argument number index        */
  721.     uint32          arg_no;                 /* Argument number              */
  722.     atr_blk         atr;                    /* The header for the atr image */
  723.     uint32          atr_siz;                /* Size of ATR file excl. hdr   */
  724.     uint32          wrk_ndx;                /* Work index                   */
  725.     bool            end_of_str;             /* Null terminator seen?        */
  726.     uint32          hdr = 'N'+'I'+'C'+'K' +
  727.                           'A'+'T'+'A'+'R'+'I';
  728.     ubyte           buf[BUF_LEN];           /* Buffer string                */
  729.     uint32          stat;                   /* Status from function         */
  730.     ubyte           proceed;                /* Proceed with conversion      */
  731.  
  732. /*
  733. **  Set default values for unused command switches.
  734. */
  735.     atr_path[0] = 0x00;
  736.     dcm_path[0] = 0x00;
  737.     diagnostics = FALSE;
  738.     statistics = FALSE;
  739.     max_sector = 0;
  740.     pass_cnt = 0;
  741.     pass_expected = 0;
  742.  
  743. /*
  744. **  Process command line arguments.
  745. **  We do not treat the options switch as an argument.  It may be placed
  746. **  anywhere on the command line.  So we have to count the arguments ourselves
  747. **  so that we know what argument we are processing.
  748. **  If this program is ported to the Atari ST, the program name is not
  749. **  on the command line, so the program should then be adapted.
  750. */
  751.     arg_no = 0;
  752.  
  753.     for( arg_ndx = 1; arg_ndx < argc; arg_ndx++ )
  754.     {
  755.  
  756. /*
  757. **  If we encounter the options switch, process the options.
  758. **  The options must start with a slash.
  759. */
  760.         if( ( argv[arg_ndx][0] == '/' ) || ( argv[arg_ndx][0] == '-' ) )
  761.         {
  762.  
  763.             for( wrk_ndx = 0; argv[arg_ndx][wrk_ndx]; wrk_ndx++ )
  764.             {
  765.  
  766. /*
  767. **  If the user is confused, seeking help, she/he should read the * manual.
  768. **  We can give them a hint though.
  769. */
  770.                 if( argv[arg_ndx][wrk_ndx] == '?' )
  771.                 {
  772.                     usage( argv[0] );
  773.                     panic( 0 );         /* Not really an error */
  774.                 }
  775.  
  776. /*
  777. **  The /d option selects the diagnostics output.
  778. */
  779.                 if( toupper( argv[arg_ndx][wrk_ndx] ) == 'D' )
  780.                 {
  781.                     diagnostics = TRUE;
  782.                     continue;
  783.                 }
  784.  
  785. /*
  786. **  The /h option selects the highest sector number we should expect.
  787. **  Diskcomm calls this the Maximum sector number your drive is capable
  788. **  of reading.  This happens to be option H in the menu.
  789. **  The format of this switch is /h=nnnn where nnnn is a numeric value
  790. **  that within Diskcomm can range from 1 to 9999.  We accept any number
  791. **  the user enters though.  This option is only needed for non-standard
  792. **  disk sizes, where we want the proper number of sectors with zeroes
  793. **  at the end of the disk image.  This cannot be used to truncate a disk.
  794. */
  795.                 if( toupper( argv[arg_ndx][wrk_ndx] ) == 'H' )
  796.                 {
  797.                     while( argv[arg_ndx][++wrk_ndx] )
  798.                     {
  799.                         if( ( argv[arg_ndx][wrk_ndx] >= '0' ) &&
  800.                             ( argv[arg_ndx][wrk_ndx] <= '9' ) )
  801.                         {
  802.                             max_sector *= 10;
  803.                             max_sector += argv[arg_ndx][wrk_ndx] - '0';
  804.                         }
  805.                     }
  806.                     break;
  807.                 }
  808.  
  809. /*
  810. **  The /s option selects the statistics output.
  811. */
  812.                 if( toupper( argv[arg_ndx][wrk_ndx] ) == 'S' )
  813.                 {
  814.                     statistics = TRUE;
  815.                     continue;
  816.                 }
  817.  
  818. /*
  819. **  Ignore other options.
  820. */
  821.                 continue;
  822.             } /* end for all characters after options switch */
  823.  
  824. /*
  825. **  No further processing for the options switches.
  826. */
  827.             continue;
  828.         } /* end if options switch */
  829.         arg_no++;
  830.  
  831. /*
  832. **  First argument is the file spec for the Diskcomm file.
  833. */
  834.         if( arg_no == 1 )
  835.         {
  836.             for ( wrk_ndx = 0, end_of_str = FALSE;
  837.                 wrk_ndx < PATH_LEN; wrk_ndx++ )
  838.             {
  839.                 if ( argv[arg_ndx][wrk_ndx] == '\0' ) /* End of argument string?            */
  840.                     end_of_str = TRUE;
  841.                 if ( end_of_str )
  842.                     dcm_path[wrk_ndx] = '\0';
  843.                 else
  844.                     dcm_path[wrk_ndx] = toupper( argv[arg_ndx][wrk_ndx] );
  845.             }
  846.         }
  847.  
  848. /*
  849. **  Optionally the name of the SIO2PC image file can be entered as the second
  850. **  command line argument.  If it is not specified, the input path is copied,
  851. **  and the extension is replaced by .atr.
  852. */
  853.         if( arg_no == 2 )
  854.         {
  855.             for ( wrk_ndx = 0, end_of_str = FALSE;
  856.                 wrk_ndx < PATH_LEN; wrk_ndx++ )
  857.             {
  858.                 if ( argv[arg_ndx][wrk_ndx] == '\0' ) /* End of argument string?            */
  859.                     end_of_str = TRUE;
  860.                 if ( end_of_str )
  861.                     atr_path[wrk_ndx] = '\0';
  862.                 else
  863.                     atr_path[wrk_ndx] = toupper( argv[arg_ndx][wrk_ndx] );
  864.             }
  865.  
  866.         }
  867.     } /* end for all command line arguments */
  868.  
  869. /*
  870. **  If there is no filename on the command line, ask for it.
  871. */
  872.     if( ( arg_no == 0 ) || ( dcm_path[0] == 0x00 ) )
  873.     {
  874.  
  875. /*
  876. **  Open hailing frequencies.
  877. **  No command line arguments, so ask what it is we have to do.
  878. */
  879.         printf( "\n\nDiskcomm DCM archive to SIO2PC ATR disk image converter.\n" );
  880.         printf( "Version February 2, 1998\n" );
  881.         printf( "\nCopyright 1998 by Ernest R. Schreurs\n" );
  882.         printf( "\nAll rights reserved\n" );
  883.  
  884.  
  885.         while( TRUE )                       /* until terminated by control Z*/
  886.         {
  887.             printf( "\nEnter ^Z or hit Control Z to terminate\n" );
  888.             if( dcm_file )
  889.             {
  890.                 fclose( dcm_file );
  891.                 dcm_file = NULL;
  892.             }
  893.             if( atr_file )
  894.             {
  895.                 fclose( atr_file );
  896.                 atr_file = NULL;
  897.             }
  898.  
  899.             do                              /* until .dcm file entered    */
  900.             {
  901.                 printf("\nEnter .dcm file to be converted : ");
  902.                 GET_BUF();
  903.  
  904.                 for ( wrk_ndx = 0, end_of_str = FALSE;
  905.                     wrk_ndx < PATH_LEN; wrk_ndx++ )
  906.                 {
  907.                     if ( wrk_ndx < BUF_LEN )
  908.                     {
  909.                         if ( buf[wrk_ndx] == '\n' ) /* End of inputted string?      */
  910.                             end_of_str = TRUE;
  911.                         if ( buf[wrk_ndx] == '\0' ) /* Overkill, End marked by \n   */
  912.                             end_of_str = TRUE;
  913.                         if ( end_of_str )
  914.                             dcm_path[wrk_ndx] = '\0';
  915.                         else
  916.                             dcm_path[wrk_ndx] = toupper( buf[wrk_ndx] );
  917.                     }
  918.                 }
  919.             } while ( dcm_path[0] == ' ' );
  920.  
  921.             do                              /* until answer is Y or N       */
  922.             {
  923.                 printf("\nConvert file %s\n", dcm_path);
  924.                 printf("\nIs this correct Y)es or N)o : ");
  925.                 GET_BUF();
  926.                 proceed = toupper( buf[0] );
  927.  
  928. /*
  929. **  If blank, default is correct
  930. */
  931.                 if ( proceed == '\n' )
  932.                     proceed = 'Y';
  933.  
  934.             } while ( proceed != 'Y' && proceed != 'N' );
  935.  
  936.             if ( proceed == 'N' )
  937.                 continue;
  938.  
  939.             dcm_file = fopen( (char *)dcm_path, "rb" );
  940.             if( dcm_file == NULL )
  941.             {
  942.                 fprintf(stderr, "Cannot open Diskcomm file\n");
  943.                 continue;
  944.             }
  945.  
  946.             do                              /* until .atr file entered    */
  947.             {
  948.                 printf("\nEnter .atr file to be created : ");
  949.                 GET_BUF();
  950.  
  951.                 for ( wrk_ndx = 0, end_of_str = FALSE;
  952.                     wrk_ndx < PATH_LEN; wrk_ndx++ )
  953.                 {
  954.                     if ( wrk_ndx < BUF_LEN )
  955.                     {
  956.                         if ( buf[wrk_ndx] == '\n' ) /* End of inputted string?      */
  957.                             end_of_str = TRUE;
  958.                         if ( buf[wrk_ndx] == '\0' ) /* Overkill, End marked by \n   */
  959.                             end_of_str = TRUE;
  960.                         if ( end_of_str )
  961.                             atr_path[wrk_ndx] = '\0';
  962.                         else
  963.                             atr_path[wrk_ndx] = toupper( buf[wrk_ndx] );
  964.                     }
  965.                 }
  966.             } while ( atr_path[0] == ' ' );
  967.  
  968.             do                              /* until answer is Y or N       */
  969.             {
  970.                 printf("\nConvert file %s to %s\n", dcm_path, atr_path);
  971.                 printf("\nIs this correct Y)es or N)o : ");
  972.                 GET_BUF();
  973.                 proceed = toupper( buf[0] );
  974.  
  975. /*
  976. **  If blank, default is correct
  977. */
  978.                 if ( proceed == '\n' )
  979.                     proceed = 'Y';
  980.  
  981.             } while ( proceed != 'Y' && proceed != 'N' );
  982.  
  983.             if ( proceed == 'N' )
  984.                 continue;
  985.  
  986. /*
  987. **  Check to see that we can open the file.
  988. */
  989.             atr_file = fopen( (char *)atr_path, "rb" );
  990.             if( atr_file != NULL )
  991.             {
  992.                 fprintf(stderr, "Specified SIO2PC image file %s already exists.\n", atr_path);
  993.                 fclose( atr_file );
  994.                 continue;
  995.             }
  996.             atr_file = fopen( (char *)atr_path, "wb+" );
  997.             if( atr_file == NULL )
  998.             {
  999.                 fprintf(stderr, "Cannot open SIO2PC image file %s\n", atr_path);
  1000.                 continue;
  1001.             }
  1002.  
  1003. /*
  1004. **  Ask for diagnostics option.
  1005. */
  1006.             do                              /* until answer is Y or N       */
  1007.             {
  1008.                 printf("\nPrint diagnostic data Y)es or N)o : ");
  1009.                 GET_BUF();
  1010.                 answer = toupper( buf[0] );
  1011.  
  1012. /*
  1013. **  If blank, default is no diagnostics
  1014. */
  1015.                 if ( answer == '\n' )
  1016.                     answer = 'N';
  1017.  
  1018.             } while ( answer != 'Y' && answer != 'N' );
  1019.  
  1020.             diagnostics = ( answer == 'Y' ) ? TRUE : FALSE;
  1021.  
  1022. /*
  1023. **  Ask for statistics option.
  1024. */
  1025.             do                              /* until answer is Y or N       */
  1026.             {
  1027.                 printf("\nPrint statistics Y)es or N)o : ");
  1028.                 GET_BUF();
  1029.                 answer = toupper( buf[0] );
  1030.  
  1031. /*
  1032. **  If blank, default is print statistics
  1033. */
  1034.                 if ( answer == '\n' )
  1035.                     answer = 'Y';
  1036.  
  1037.             } while ( answer != 'Y' && answer != 'N' );
  1038.  
  1039. /*
  1040. **  Ask for highest sector number.
  1041. */
  1042.             printf("\nEnter highest sector number : ");
  1043.             GET_BUF();
  1044.             max_sector = 0;
  1045.  
  1046.             for( wrk_ndx = 0; wrk_ndx < BUF_LEN; wrk_ndx++ )
  1047.             {
  1048.                 if ( buf[wrk_ndx] == '\n' ) /* End of inputted string?      */
  1049.                     break;
  1050.                 if ( buf[wrk_ndx] == '\0' ) /* Overkill, End marked by \n   */
  1051.                     break;
  1052.                 if( ( buf[wrk_ndx] >= '0' ) &&
  1053.                     ( buf[wrk_ndx] <= '9' ) )
  1054.                 {
  1055.                     max_sector *= 10;
  1056.                     max_sector += buf[wrk_ndx] - '0';
  1057.                 }
  1058.             }
  1059.  
  1060.             printf( "\nConverting data from Diskcomm file.\n" );
  1061.             break;
  1062.         } /* end while need a valid filename */
  1063.     } /* end if no command line arguments */
  1064.     else
  1065.     {
  1066.  
  1067. /*
  1068. **  Replace .dcm extension by .atr extension, or add .atr if there is
  1069. **  no extension at all.  If it all fits that is.
  1070. **  If the user really wants, it is possible to make this code fail.
  1071. **  That is their loss.
  1072. */
  1073.         if( atr_path[0] == 0x00 )
  1074.         {
  1075.             memcpy( atr_path, dcm_path, PATH_LEN );
  1076.             wrk_ndx = STRLEN( atr_path );
  1077.             if( wrk_ndx > 4 )
  1078.                 if( atr_path[wrk_ndx - 4] == '.' )
  1079.                     wrk_ndx -= 4;
  1080.             if( wrk_ndx + 5 <= PATH_LEN )
  1081.                 memcpy( &(atr_path[wrk_ndx]), ".ATR", 5 );
  1082.         }
  1083.  
  1084. /*
  1085. **  Try to open the file that was specified on the command line.
  1086. */
  1087.         dcm_file = fopen( (char *)dcm_path, "rb" );
  1088.         if( dcm_file == NULL )
  1089.         {
  1090.             fprintf(stderr, "Cannot open Diskcomm file %s\n", dcm_path);
  1091.             panic( 255 );
  1092.         }
  1093.  
  1094.         atr_file = fopen( (char *)atr_path, "rb" );
  1095.         if( atr_file != NULL )
  1096.         {
  1097.             fprintf(stderr, "Specified SIO2PC image file %s already exists.\n", atr_path);
  1098.             fclose( atr_file );
  1099.             panic( 255 );
  1100.         }
  1101.         atr_file = fopen( (char *)atr_path, "wb+" );
  1102.         if( atr_file == NULL )
  1103.         {
  1104.             fprintf(stderr, "Cannot open SIO2PC image file %s\n", atr_path);
  1105.             panic( 255 );
  1106.         }
  1107.     } /* end else if no command line arguments */
  1108.  
  1109. /*
  1110. **  For multi file archives, determine the character to be incremented
  1111. **  in the path specification.
  1112. **  Try to find a "1" or an "A" starting at the end of the path
  1113. **  specification, working our way back.
  1114. **  If we hit the directory marker, use the last character before the file
  1115. **  extension.  If there is no extension, us the last character in the
  1116. **  filename.
  1117. */
  1118.     increment = PATH_LEN;
  1119.  
  1120.     for( wrk_ndx = STRLEN( dcm_path); wrk_ndx--; )
  1121.     {
  1122.         if( ( dcm_path[wrk_ndx] == '1' ) ||
  1123.             ( dcm_path[wrk_ndx] == 'A' ) )
  1124.         {
  1125.             increment = wrk_ndx;
  1126.             break;
  1127.         }
  1128.         if( ( dcm_path[wrk_ndx] == '.' ) &&
  1129.             ( wrk_ndx > 0 ) )
  1130.         {
  1131.             increment = wrk_ndx - 1;
  1132.             continue;
  1133.         }
  1134.         if( ( dcm_path[wrk_ndx] == '\\' ) ||
  1135.            ( dcm_path[wrk_ndx] == '/' ) )
  1136.         {
  1137.             if( increment == PATH_LEN )
  1138.                 increment = STRLEN( dcm_path );
  1139.             break;
  1140.         }
  1141.     }
  1142.  
  1143. /*
  1144. **  Initialize some stuff.
  1145. */
  1146.     memset( sec_zer, 0x00, 256 );
  1147.     sector_atr = 0;
  1148.     cnt_41 = 0;
  1149.     cnt_42 = 0;
  1150.     cnt_43 = 0;
  1151.     cnt_44 = 0;
  1152.     cnt_45 = 0;
  1153.     cnt_46 = 0;
  1154.     cnt_47 = 0;
  1155.     cnt_F9 = 0;
  1156.     cnt_FA = 0;
  1157.  
  1158.     do          /* until last pass processed */
  1159.     {
  1160.  
  1161. /*
  1162. **  Process the header of the .dcm file.
  1163. */
  1164.         pass_cnt++;
  1165.         if( pass_cnt & 0x20 )
  1166.             pass_expected = pass_cnt - 1;       /* For some odd reason */
  1167.         else
  1168.             pass_expected = pass_cnt;           /* Pass we should find next */
  1169.         pass_expected &= 0x1F;
  1170.  
  1171.         stat = process_header();
  1172.         if( stat == FAILURE )
  1173.         {
  1174.             panic( 255 );
  1175.         }
  1176.  
  1177.         PRINT( ("File : %s\n", dcm_path ) );
  1178.         PRINT( ("Density: %u\n", density ) );
  1179.  
  1180. /*
  1181. **  Based on the density, compute the number of sectors and the
  1182. **  sector size.  Note that the user can modify the highest sector number
  1183. **  as one of the options in Diskcomm.  So these sector counts are only
  1184. **  defaults.  Diskcomm might even be able to compress an entire hard disk.
  1185. */
  1186.         switch( density )
  1187.         {
  1188.             case DENSITY_SD:
  1189.                 sectors = 720;
  1190.                 sec_size = 128;
  1191.                 break;
  1192.             case DENSITY_ED:
  1193.                 sectors = 1040;
  1194.                 sec_size = 128;
  1195.                 break;
  1196.             case DENSITY_DD:
  1197.                 sectors = 720;
  1198.                 sec_size = 256;
  1199.                 break;
  1200.             default:
  1201.                 sectors = 720;
  1202.                 sec_size = 128;
  1203.                 break;
  1204.         }
  1205.  
  1206. /*
  1207. **  If non-standard size specified, use it instead.
  1208. */
  1209.         if( max_sector )
  1210.             sectors = max_sector;
  1211.  
  1212. /*
  1213. **  The size of the ATR file is specified in paragraphs, a paragraph on the
  1214. **  PC is 16 bytes.  The size of the header, which is always 16 bytes,
  1215. **  is not included in this size value.  The size of the ATR file can thus
  1216. **  be computed by multiplying the total number of sectors by the sector size,
  1217. **  and dividing this value by 16.  Unless it is a double density disk.
  1218. **  The first three sectors of any Atari double density disk are always 
  1219. **  128 bytes in size, so that the machine can always boot these three
  1220. **  sectors.  In ATR files, these three sectors are stored as 128 bytes
  1221. **  of data.  So this must be subtracted to compute the proper size.
  1222. */
  1223.         atr_siz = sectors * sec_size;
  1224.         if( density == DENSITY_DD )
  1225.         {
  1226.             if( sectors > 3 )
  1227.                 atr_siz -= 3 * 128;
  1228.             else
  1229.                 atr_siz = sectors * 128;
  1230.         }
  1231.         atr_siz = atr_siz >> 4;     /* Convert to paragraphs, divide by 16 */
  1232.  
  1233.  
  1234. /*
  1235. **  Write the header to the SIO2PC image file.
  1236. */
  1237.         if( sector_atr == 0 )
  1238.         {
  1239.             memset( &atr, 0x00, sizeof( atr_blk ) );
  1240.             atr.atr_hdr_lo = hdr;           /* Header, sum of NICKATARI     */
  1241.             atr.atr_hdr_hi = hdr >> 8;
  1242.             atr.atr_siz_lo = atr_siz;       /* Size of atr excluding header */
  1243.             atr.atr_siz_hi = atr_siz >> 8;
  1244.             atr.atr_sec_size_lo = sec_size; /* Sector size                  */
  1245.             atr.atr_sec_size_hi = sec_size >> 8;
  1246.             atr.atr_hi_size_lo = atr_siz >> 16; /* High order part of size  */
  1247.             atr.atr_hi_size_hi = atr_siz >> 24;
  1248.             atr.atr_d_info = 0;         /* Copy protect/write protect flags */
  1249.             atr.atr_sec_lo = 0;         /* Sector number of typical copy    */
  1250.             atr.atr_sec_hi = 0;         /* protected sector, low/high       */
  1251.  
  1252.             fwrite( &atr, 1, sizeof( atr_blk ), atr_file );
  1253.         }
  1254.  
  1255. /*
  1256. **  Process the data portion of the .dcm file.
  1257. */
  1258.         stat = dcm2atr();
  1259.  
  1260.     } while( !last );
  1261.  
  1262. /*
  1263. **  Make sure the last sectors of the disk are zeroes.
  1264. */
  1265.     while( ++sector_atr <= sectors )
  1266.     {
  1267.         fwrite( sec_zer, 1, (sector_atr < 4) ? 128 : sec_size, atr_file );
  1268.     }
  1269.  
  1270. /*
  1271. **  If this is a non-standard diskette size, the actual size must be
  1272. **  adjusted in the header.  We have no way of knowing the physical
  1273. **  ending sector, if the user did not specify this.  In that case, the
  1274. **  last non-zero sector determines the disk size.  If the user did
  1275. **  specify this, this will have been recorded in the header already,
  1276. **  and we only have to adjust it if the user made a mistake.
  1277. */
  1278.     sector_atr--;
  1279.     if( sector_atr > sectors )
  1280.     {
  1281.         atr_siz = sector_atr * sec_size;        /* Compute real size */
  1282.         if( density == DENSITY_DD )
  1283.         {
  1284.             if( sector_atr > 3 )
  1285.                 atr_siz -= 3 * 128;
  1286.             else
  1287.                 atr_siz = sector_atr * 128;
  1288.         }
  1289.         atr_siz = atr_siz >> 4;     /* Convert to paragraphs, divide by 16 */
  1290.  
  1291. /*
  1292. **  Update header and write it out again.
  1293. */
  1294.         atr.atr_siz_lo = atr_siz;           /* Size of atr excluding header */
  1295.         atr.atr_siz_hi = atr_siz >> 8;
  1296.         atr.atr_hi_size_lo = atr_siz >> 16; /* High order portion of size   */
  1297.         atr.atr_hi_size_hi = atr_siz >> 24;
  1298.  
  1299.         fseek( atr_file, 0L, SEEK_SET );    /* Go back to beginning of file */
  1300.         fwrite( &atr, 1, sizeof( atr_blk ), atr_file );
  1301.     }
  1302.  
  1303.     cleanup();
  1304.  
  1305.     if( statistics )
  1306.     {
  1307.         printf("\nStatistics for Diskcomm archive file %s:\n\n", dcm_path );
  1308.         switch( density )
  1309.         {
  1310.             case DENSITY_SD:
  1311.                 printf("Single density");
  1312.                 break;
  1313.             case DENSITY_DD:
  1314.                 printf("Double density");
  1315.                 break;
  1316.             case DENSITY_ED:
  1317.                 printf("Enhanced density");
  1318.                 break;
  1319.             default:
  1320.                 printf("Unknown density");
  1321.                 break;
  1322.         }
  1323.         printf(" %lu Kbyte.\n", atr_siz >> 6 );
  1324.         printf("FA large header   : %lu\n", cnt_FA );
  1325.         printf("F9 small header   : %lu\n", cnt_F9 );
  1326.         printf("41 modify begin   : %lu\n", cnt_41 );
  1327.         printf("42 123 byte/5 byte: %lu\n", cnt_42 );
  1328.         printf("43 compressed     : %lu\n", cnt_43 );
  1329.         printf("44 modify end     : %lu\n", cnt_44 );
  1330.         printf("45 flush buffer   : %lu\n", cnt_45 );
  1331.         printf("46 identical      : %lu\n", cnt_46 );
  1332.         printf("47 uncompressed   : %lu\n", cnt_47 );
  1333.         printf("   Total number   : %lu\n", cnt_41 + cnt_42 + cnt_43 +
  1334.                                             cnt_44 + cnt_46 + cnt_47 );
  1335.         printf("   Disk sectors   : %u\n", sector_atr );
  1336.  
  1337.         printf("\nDone processing.\n");
  1338.     }
  1339.  
  1340.     return 0;
  1341. }
  1342.  
  1343. /*****************************************************************************
  1344. **  MODIFICATION HISTORY
  1345. **
  1346. **  DATE        BY   Description
  1347. **  ----------  ---  ---------------------------------------------------------
  1348. **  1998/01/04  ERS  Start coding, after many hours of research
  1349. **  1998/01/18  ERS  Release 01.00
  1350. **  1998/02/02  ERS  Release 01.01 Initialize previous sector buffer when
  1351. **                   processing next pass for multi-file archives.
  1352. **
  1353. *****************************************************************************/
  1354.